//
// Created by root on 5/14/17.
//

#include <stdlib.h>
#include "runner_lex.h"

int setup_lex(void **state) {
    construct_rules();
    init_lex_orders();
    cache_patterns();
    return 0;
}

int teardown_lex(void **state) {
    if (rules != NULL) {
        hashmap_free(rules);
    }
    if (lexOrders != NULL) {
        free(lexOrders);
    }
    return 0;
}

static Pattern *construct_pattern(String regex, String target) {
    Pattern *p = (Pattern *) malloc(sizeof(Pattern));
    p->regex = regex;
    p->target = target;
    return p;
}

void construct_rules() {
    init_lexer();

    Queue *atom_patterns = init_queue();
    enqueue(atom_patterns, construct_pattern("<boolean>", "($)"));
    enqueue(atom_patterns, construct_pattern("<character>", "($)"));
    enqueue(atom_patterns, construct_pattern("<string>", "($)"));
    enqueue(atom_patterns, construct_pattern("<number>", "($)"));
    enqueue(atom_patterns, construct_pattern("<symbol>", "($)"));
    register_rule("atom", atom_patterns);

    Queue *boolean_patterns = init_queue();
    enqueue(boolean_patterns, construct_pattern("<bool>", "(lex \"Boolean\" \"$1\")"));
    register_rule("boolean", boolean_patterns);

    Queue *character_patterns = init_queue();
    enqueue(character_patterns, construct_pattern("<char>", "(lex \"Character\" \"$1\")"));
    register_rule("character", character_patterns);

    Queue *string_patterns = init_queue();
    enqueue(string_patterns, construct_pattern("<str>", "(lex \"String\" \"$1\")"));
    register_rule("string", string_patterns);

    Queue *symbol_patterns = init_queue();
    enqueue(symbol_patterns, construct_pattern("(<initial><subsequent>*)", "(lex \"Symbol\" \"$\")"));
    enqueue(symbol_patterns, construct_pattern("(<peculiar>)", "(lex \"Symbol\" \"$\")"));
    register_group("symbol", symbol_patterns);

    Queue *number_patterns = init_queue();
    enqueue(number_patterns, construct_pattern("<complex>", "($)"));
    register_rule("number", number_patterns);

    Queue *complex_patterns = init_queue();
    enqueue(complex_patterns, construct_pattern("<real>", "(make-complex $1 (lex \"Integer\" \"0\"))"));
    enqueue(complex_patterns, construct_pattern("<real>\\+<ureal>i", "(make-complex $1 $2)"));
    enqueue(complex_patterns, construct_pattern("<real>-<ureal>i", "(make-complex $1 (- $2))"));
    enqueue(complex_patterns, construct_pattern("<real>\\+i", "(make-complex $1 (lex \"Integer\" \"1\"))"));
    enqueue(complex_patterns, construct_pattern("<real>-i", "(make-complex $1 (lex \"Integer\" \"-1\"))"));
    enqueue(complex_patterns, construct_pattern("\\+<ureal>i", "(make-complex (lex \"Integer\" \"0\") $1)"));
    enqueue(complex_patterns, construct_pattern("-<ureal>i", "(make-complex (lex \"Integer\" \"0\") (- $1))"));
    enqueue(complex_patterns, construct_pattern("\\+i", "(make-complex (lex \"Integer\" \"0\") (lex \"Integer\" \"1\"))"));
    enqueue(complex_patterns, construct_pattern("-i", "(make-complex (lex \"Integer\" \"0\") (lex \"Integer\" \"-1\"))"));
    register_rule("complex", complex_patterns);

    Queue *real_patterns = init_queue();
    enqueue(real_patterns, construct_pattern("<integer>", "($)"));
    enqueue(real_patterns, construct_pattern("<fraction>", "($)"));
    enqueue(real_patterns, construct_pattern("<decimal>", "($)"));
    register_rule("real", real_patterns);

    Queue *ureal_patterns = init_queue();
    enqueue(ureal_patterns, construct_pattern("<uinteger>", "($)"));
    enqueue(ureal_patterns, construct_pattern("<ufraction>", "($)"));
    enqueue(ureal_patterns, construct_pattern("<udecimal>", "($)"));
    register_rule("ureal", ureal_patterns);

    Queue *fraction_patterns = init_queue();
    enqueue(fraction_patterns, construct_pattern("<integer>\\/<uinteger>", "(make-fraction $1 $2)"));
    register_rule("fraction", fraction_patterns);

    Queue *ufraction_patterns = init_queue();
    enqueue(ufraction_patterns, construct_pattern("<uinteger>\\/<uinteger>", "(make-fraction $1 $2)"));
    register_rule("ufraction", ufraction_patterns);

    Queue *integer_patterns = init_queue();
    enqueue(integer_patterns, construct_pattern("(<sign><digit>+)", "(lex \"Integer\" \"$\")"));
    register_group("integer", integer_patterns);

    Queue *uinteger_patterns = init_queue();
    enqueue(uinteger_patterns, construct_pattern("(<digit>+)", "(lex \"Integer\" \"$\")"));
    register_group("uinteger", uinteger_patterns);

    Queue *decimal_patterns = init_queue();
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign>\\.<digit>+)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+\\.<digit>*)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+\\.)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign>\\.<digit>+<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+\\.<digit>*<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(decimal_patterns, construct_pattern("(<sign><digit>+\\.<suffix>)", "(lex \"Decimal\" \"$\")"));
    register_group("decimal", decimal_patterns);

    Queue *udecimal_patterns = init_queue();
    enqueue(udecimal_patterns, construct_pattern("(<digit>+)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(\\.<digit>+)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(<digit>+\\.<digit>*)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(<digit>+\\.)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(<digit>+<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(\\.<digit>+<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(<digit>+\\.<digit>*<suffix>)", "(lex \"Decimal\" \"$\")"));
    enqueue(udecimal_patterns, construct_pattern("(<digit>+\\.<suffix>)", "(lex \"Decimal\" \"$\")"));
    register_group("udecimal", udecimal_patterns);

    register_token("bool", "#[tf]");
    register_token("char", "#\\\\[[:graph:]]|#\\\\space|#\\\\newline");
    //register_token("str", "\"(\\.|[^\"])*\"");
    //see https://stackoverflow.com/questions/249791/regex-for-quoted-string-with-escaping-quotes
    register_token("str", "\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"");
    register_token("suffix", "e[+-]?[[:digit:]]+");
    register_token("sign", "[+-]?");
    register_token("digit", "[[:digit:]]");
    register_token("initial", "[a-zA-Z!$%&*/:<=>?~_^]");
    register_token("subsequent", "[a-zA-Z0-9!$%&*/:<=>?~_^.+-]");
    register_token("peculiar", "\\+|-|...");
    register_token("letter", "[a-zA-Z]");
}

void init_lex_orders() {
    String *orders = (String *)malloc(9 * sizeof(String));
    orders[0] = copy_string("boolean");
    orders[1] = copy_string("character");
    orders[2] = copy_string("string");
    orders[3] = copy_string("integer");
    orders[4] = copy_string("fraction");
    orders[5] = copy_string("decimal");
    orders[6] = copy_string("complex");
    orders[7] = copy_string("symbol");
    orders[8] = NULL;
    lexOrders = orders;
}